import { VitePlugin } from "@/utils.ts"
import { readFileSync } from "node:fs"
import { dirname, resolve } from "node:path"
import { RolldownPlugin } from "rolldown"

/**
 * tsconfig.json 中关于别名的必要字段。
 * 这里只定义了必要的字段，
 * 所有其他字段都被省略了。
 */
export type TsconfigAliases = {
  compilerOptions?: {
    baseUrl?: string
    paths?: Record<string, string[]>
  }
}

/**
 * 解析 tsconfig.json 文件或 {@link TsconfigAliases} 对象。
 *
 * 1. `undefined`: 使用当前工作目录中的 `tsconfig.json` 文件。
 * 2. `string`: 指定的 tsconfig.json 文件路径。
 * 3. `object`: 指定的 {@link TsconfigAliases} 对象。
 *
 * 目前无法解析 tsconfig.json 中的 `extends` 字段。
 *
 * 此函数封装了 {@link tsconfigAliasesFromFile}
 * 和 {@link tsconfigAliasesFromOptions} 以便使用。
 * 存在类型守卫可能会导致性能问题。
 * 对于性能敏感的场景，
 * 您可以直接调用这两个函数。
 *
 * @param tsconfig tsconfig.json 文件路径或 {@link TsconfigAliases} 对象。
 * @returns 解析后的别名，包含 {@link resolve} 解析后的路径。
 */
export function tsconfigAliases(tsconfig?: string | TsconfigAliases) {
  return !tsconfig || typeof tsconfig === "string"
    ? tsconfigAliasesFromFile(tsconfig as string | undefined)
    : tsconfigAliasesFromOptions(tsconfig as TsconfigAliases)
}

/**
 * 解析 tsconfig.json 文件以获取别名。
 *
 * 1. `undefined`: 使用当前工作目录中的 `tsconfig.json` 文件。
 * 2. `string`: 指定的 tsconfig.json 文件路径。
 * 3. 所有返回的路径都将被 {@link resolve} 解析。
 *
 * 目前无法解析 tsconfig.json 中的 `extends` 字段。
 *
 * @param file tsconfig.json 文件路径，默认为 "tsconfig.json"。
 * @returns 解析后的别名，包含解析后的路径。
 */
export function tsconfigAliasesFromFile(file = "tsconfig.json") {
  const raw = readFileSync(file || "tsconfig.json", "utf-8")
  const options = JSON.parse(raw) as TsconfigAliases
  return tsconfigAliasesFromOptions(options, file)
}
/**
 * 解析 {@link TsconfigAliases} 对象以获取别名。
 *
 * 对象可能为 undefined，
 * 甚至必要字段也可能为 undefined，
 * 这只会导致返回空对象。
 * 在这种情况下不会抛出错误。
 *
 * 目前无法解析 tsconfig.json 中的 `extends` 字段。
 *
 * @param options {@link TsconfigAliases} 对象。
 * @param filePath tsconfig.json 文件所在的路径，默认为 "tsconfig.json"。
 * @returns 解析后的别名，包含解析后的路径。
 */
export function tsconfigAliasesFromOptions(
  options?: TsconfigAliases,
  filePath = "tsconfig.json",
) {
  const { baseUrl, paths } = options?.compilerOptions || {}
  const result: Record<string, string[]> = {}
  for (const [key, value] of Object.entries(paths || {})) {
    if (value.length > 0) {
      result[key.replace(/\/\*$/g, "")] = value.map((path) =>
        resolve(
          dirname(filePath),
          baseUrl || "",
          path.replace(/\/\*$/g, "").replace(/\*/g, ""),
        ),
      )
    }
  }
  return result
}

/**
 * 一个用于解析别名的 rolldown 插件。
 *
 * 1. `undefined`: 使用当前工作目录中的 `tsconfig.json` 文件。
 * 2. `string`: 指定的 tsconfig.json 文件路径。
 * 3. `object`: 指定的 {@link TsconfigAliases} 对象。
 *
 * 目前无法解析 tsconfig.json 中的 `extends` 字段。
 *
 * @param tsconfig tsconfig.json 文件路径或 {@link TsconfigAliases} 对象。
 * @returns 包含别名的 rolldown 插件。
 */
export function aliases(tsconfig?: string | TsconfigAliases): RolldownPlugin {
  return {
    name: "aliases",
    options(inputOptions) {
      return {
        ...inputOptions,
        resolve: {
          alias: {
            ...tsconfigAliases(tsconfig),
            ...inputOptions.resolve?.alias,
          },
        },
      }
    },
  }
}

/**
 * 一个用于解析别名的 Vite 兼容插件。
 *
 * 1. `undefined`: 使用当前工作目录中的 `tsconfig.json` 文件。
 * 2. `string`: 指定的 tsconfig.json 文件路径。
 * 3. `object`: 指定的 {@link TsconfigAliases} 对象。
 *
 * 目前无法解析 tsconfig.json 中的 `extends` 字段。
 *
 * @param tsconfig tsconfig.json 文件路径或 {@link TsconfigAliases} 对象。
 * @returns 包含别名的 Vite 兼容插件。
 */
export function viteAliases(tsconfig?: string | TsconfigAliases): VitePlugin {
  return {
    name: "vite-aliases",
    config() {
      const aliases = tsconfigAliases(tsconfig)
      const result: Record<string, string> = {}

      // 对于每个别名，使用第一个路径（Vite 不支持多个路径）。
      for (const [key, paths] of Object.entries(aliases)) {
        if (paths.length > 0) result[key] = paths[0]
      }
      return { resolve: { alias: result } }
    },
  }
}
